/*
 * Author: Honrun
 */
#include "stm32f4xx_hal.h"
#include "stm32f4xx_ll_dma.h"
#include "DevicesADC.h"


static uint16_t st_ADC1DmaDatas[ADC1_SAMPLING_NUMBER][ADC1_CHANNEL_NUMBER] = {0};

/* ADC handler declaration */
static ADC_HandleTypeDef st_AdcHandle = {0};
static DMA_HandleTypeDef st_AdcDmaHandle = {0};


static void vADCDMAInit(void);


void vADC1Init(void)
{
    ADC_ChannelConfTypeDef sConfig = {0};
    GPIO_InitTypeDef GPIO_InitStruct = {0};

    st_AdcHandle.Instance = ADC1;

    /* Enable GPIO clock */
    __HAL_RCC_GPIOA_CLK_ENABLE();
    __HAL_RCC_GPIOC_CLK_ENABLE();
    /* ADCx Periph clock enable */
    __HAL_RCC_ADC1_CLK_ENABLE();


    /* ADC3 Channel8 GPIO pin configuration */
    GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Pin = GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_3 | GPIO_PIN_4;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

    GPIO_InitStruct.Pin = GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_3;
    HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);


    /* Configure the ADC peripheral */
    st_AdcHandle.Instance                   = ADC1;
    st_AdcHandle.Init.ClockPrescaler        = ADC_CLOCK_SYNC_PCLK_DIV4;
    st_AdcHandle.Init.Resolution            = ADC_RESOLUTION_12B;
    st_AdcHandle.Init.ScanConvMode          = ENABLE;                           /* Sequencer disabled (ADC conversion on only 1 channel: channel set on rank 1) */
    st_AdcHandle.Init.ContinuousConvMode    = ENABLE;                           /* Continuous mode disabled to have only 1 conversion at each conversion trig */
    st_AdcHandle.Init.DiscontinuousConvMode = DISABLE;                          /* Parameter discarded because sequencer is disabled */
    st_AdcHandle.Init.NbrOfDiscConversion   = 0;
    st_AdcHandle.Init.ExternalTrigConvEdge  = ADC_EXTERNALTRIGCONVEDGE_NONE;    /* Conversion start trigged at each external event */
    st_AdcHandle.Init.ExternalTrigConv      = ADC_SOFTWARE_START;
    st_AdcHandle.Init.DataAlign             = ADC_DATAALIGN_RIGHT;
    st_AdcHandle.Init.NbrOfConversion       = ADC1_CHANNEL_NUMBER;
    st_AdcHandle.Init.DMAContinuousRequests = ENABLE;
    st_AdcHandle.Init.EOCSelection          = DISABLE;
    HAL_ADC_Init(&st_AdcHandle);


    /* 电压采集通道 */
    sConfig.Channel      = ADC_CHANNEL_1;
    sConfig.Rank         = ADC_CHANNEL_VOLTAGE_VCC1;
    HAL_ADC_ConfigChannel(&st_AdcHandle, &sConfig);

    sConfig.Channel      = ADC_CHANNEL_4;
    sConfig.Rank         = ADC_CHANNEL_VOLTAGE_VCC2H;
    HAL_ADC_ConfigChannel(&st_AdcHandle, &sConfig);

    sConfig.Channel      = ADC_CHANNEL_3;
    sConfig.Rank         = ADC_CHANNEL_VOLTAGE_VCC2L;
    HAL_ADC_ConfigChannel(&st_AdcHandle, &sConfig);

    sConfig.Channel      = ADC_CHANNEL_2;
    sConfig.Rank         = ADC_CHANNEL_VOLTAGE_VCCQ;
    HAL_ADC_ConfigChannel(&st_AdcHandle, &sConfig);

    /* 电流采集通道 */
    sConfig.SamplingTime = ADC_SAMPLE_TIME;
    sConfig.Offset       = 0;
    sConfig.Channel      = ADC_CHANNEL_10;
    sConfig.Rank         = ADC_CHANNEL_CURRENT_VCC1;
    HAL_ADC_ConfigChannel(&st_AdcHandle, &sConfig);

    sConfig.Channel      = ADC_CHANNEL_11;
    sConfig.Rank         = ADC_CHANNEL_CURRENT_VCC2H;
    HAL_ADC_ConfigChannel(&st_AdcHandle, &sConfig);

    sConfig.Channel      = ADC_CHANNEL_13;
    sConfig.Rank         = ADC_CHANNEL_CURRENT_VCC2L;
    HAL_ADC_ConfigChannel(&st_AdcHandle, &sConfig);

    sConfig.Channel      = ADC_CHANNEL_12;
    sConfig.Rank         = ADC_CHANNEL_CURRENT_VCCQ;
    HAL_ADC_ConfigChannel(&st_AdcHandle, &sConfig);


    vADCDMAInit();


    /* 开启ADCx转换 */
    HAL_ADC_Start(&st_AdcHandle);
}

static void vADCDMAInit(void)
{
    st_AdcDmaHandle.Instance = DMA2_Stream0;

    /* Enable DMA2 clock */
    __HAL_RCC_DMA2_CLK_ENABLE();


    HAL_DMA_DeInit(&st_AdcDmaHandle);
    /* Set the parameters to be configured */
    st_AdcDmaHandle.Instance = DMA2_Stream0;
    st_AdcDmaHandle.Init.Channel  = DMA_CHANNEL_0;
    st_AdcDmaHandle.Init.Direction = DMA_PERIPH_TO_MEMORY;
    st_AdcDmaHandle.Init.PeriphInc = DMA_PINC_DISABLE;
    st_AdcDmaHandle.Init.MemInc = DMA_MINC_ENABLE;
    st_AdcDmaHandle.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
    st_AdcDmaHandle.Init.MemDataAlignment = DMA_PDATAALIGN_HALFWORD;
    st_AdcDmaHandle.Init.Mode = DMA_CIRCULAR;
    st_AdcDmaHandle.Init.Priority = DMA_PRIORITY_HIGH;
    st_AdcDmaHandle.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
    st_AdcDmaHandle.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_HALFFULL;
    st_AdcDmaHandle.Init.MemBurst = DMA_MBURST_SINGLE;
    st_AdcDmaHandle.Init.PeriphBurst = DMA_PBURST_SINGLE;
    HAL_DMA_Init(&st_AdcDmaHandle);

    /* Associate the initialized DMA handle to the the ADC handle */
    __HAL_LINKDMA(&st_AdcHandle, DMA_Handle, st_AdcDmaHandle);


    /* Enable the DMA stream */
    HAL_DMA_Start(&st_AdcDmaHandle, (uint32_t)(&(ADC1->DR)), (uint32_t)st_ADC1DmaDatas, ADC1_CHANNEL_NUMBER * ADC1_SAMPLING_NUMBER);


    /* Enable ADC DMA mode */
    SET_BIT(ADC1->CR2, ADC_CR2_DMA);
}

float fADCGetValue(uint8_t ucChannel)
{
    uint32_t uiValueSum = 0, i = 0;
    uint16_t *pDatasHand = (uint16_t *)st_ADC1DmaDatas;

    if(ucChannel > ADC1_CHANNEL_NUMBER)
        return 0.0f;

    pDatasHand += ucChannel - 1;

    for(i = 0; i < ADC1_SAMPLING_NUMBER; ++i)
    {
        uiValueSum += *pDatasHand;

        pDatasHand += ADC1_CHANNEL_NUMBER;
    }

    return ((float)uiValueSum / ADC1_SAMPLING_NUMBER * (3.3f / 4095.0f));
}
